/**
 * 
 */
package com.android.dwt;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.provider.Settings;
import android.util.Log;

import com.android.dwt.service.DWTService;
import com.android.dwt.service.DWTService.MyBinder;
import com.android.dwt.util.C;

/**
 * @author xiang
 *
 */
public class MyLocationManager implements LocationListener, ServiceConnection {
	
	private static final String TAG = "MyLocationManager";
	public final float ALERT_RADIUS = 50.0f;		// radius of location alert
	
	private LocationManager mLocationMgr;
	private Context mContext;
	private Location mLocation;
	private String mProvider;
	private double mLat, mLng;
	private String mAddress;
	private boolean mProviderEnabled;
	private DWTService mService;
	private Handler mHandler;

//	private MyReceiver mReceiver;
	
	
	public MyLocationManager(Context context) {
		
		this.mContext = context;
		mLocationMgr = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
	}
	
	public void bindGPS() {
		
		Log.e(TAG, "bind GPS!");
		
		if (mLocationMgr == null) {
			
			mLocationMgr = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
		}
		
		if (isProviderEnabled()) {
			
			getProvider();
			
			mLocation = mLocationMgr.getLastKnownLocation(mProvider);
    		
    		mLocationMgr.requestLocationUpdates(mProvider, 2000, 5, this);
		}
		
		return;
	}
	
	/*
	 * Rebind GPS service, in case that $mProvider is not null
	 */
	public void reBindGPS() {
		
		Log.e(TAG, "re-bind GPS!");
		
		if (mLocationMgr == null) {
			
			mLocationMgr = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
		}
		
		getProvider();
		
		mLocation = mLocationMgr.getLastKnownLocation(mProvider);
		
		mLocationMgr.requestLocationUpdates(mProvider, 2000, 10, this);
	}
	
	public void unbindGPS() {
		
		Log.e(TAG, "unbind GPS!");
		
		// Remove listener
		mLocationMgr.removeUpdates(this);
		mLocationMgr = null;
	}
	
	public LocationManager getLocationManager() {
		
		return mLocationMgr;
	}
	
	public String getCurAddress() {
		
		return mAddress;
	}
	
	public boolean isProviderEnabled() {
		
		boolean bGPS = mLocationMgr.isProviderEnabled(LocationManager.GPS_PROVIDER);
		boolean bNetwork = mLocationMgr.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

		Log.e(TAG, "bGPS=" + bGPS + " bNetwork=" + bNetwork);
		
		if (bGPS || bNetwork) {
			
			return true;
			
		} else {
			
			return false;
		}
		
	} 
	
	/*
	 * Get GPS provider
	 */
	public void getProvider() {
		
		Criteria criteria = new Criteria();

		criteria.setAccuracy(Criteria.ACCURACY_FINE);
		criteria.setAltitudeRequired(false);
		criteria.setBearingRequired(false);
		criteria.setCostAllowed(true);
		criteria.setPowerRequirement(Criteria.POWER_LOW);

		mProvider = mLocationMgr.getBestProvider(criteria, true);
	}
	
	/*
	 * Get Address string by latitude and longtitude
	 */
	public String getAddressByLatLng(double lat, double lng) {
		
		String address = null;
		JSONObject jsonObject = geocodeAddr(lat, lng);
		
		try {
			
			JSONArray placemarks = jsonObject.getJSONArray("Placemark");
			JSONObject place = placemarks.getJSONObject(0);
			address = place.getString("address");
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return address;
	}
	
	/*
	 * Get JSON Object by latitude and longtitude
	 */
	private JSONObject geocodeAddr(double latitude, double longtitude) {
		
		String urlString = "http://ditu.google.com/maps/geo?q=+"
				+ latitude + "," + longtitude
				+ "&output=json&oe=utf8&hl=zh-CN&sensor=false";
		
//		String urlString = "http://maps.google.com/maps/api/geocode/json?latlng="
//				+ latitude + "," + longtitude
//				+ "&sensor=false";
		
		StringBuilder sTotalString = new StringBuilder();
		
		Log.e(TAG, "urlString: "+urlString);
		
		try {

			URL url = new URL(urlString);
			URLConnection connection = url.openConnection();
			HttpURLConnection httpConnection = (HttpURLConnection) connection;

			InputStream urlStream = httpConnection.getInputStream();
			BufferedReader bufferedReader = new BufferedReader(
					new InputStreamReader(urlStream));

			String sCurrentLine = "";
			while ((sCurrentLine=bufferedReader.readLine()) != null) {
				sTotalString.append(sCurrentLine);
			}
			
			bufferedReader.close();
			httpConnection.disconnect();

		} catch (Exception e1) {
			// TODO Auto-generated
			e1.printStackTrace();
		}

		JSONObject jsonObject = new JSONObject();
		try {
			jsonObject = new JSONObject(sTotalString.toString());
			
		} catch (JSONException e) {
			
			e.printStackTrace();
		}

		return jsonObject;
	}
	
	/*
	 * Set location alert on a specific location
	 */
	public void setProximityAlert(Position loc, long taskId, TaskItem task) {
		
		double lat = loc.getLatitude()/1e6;
		double lng = loc.getLongtitude()/1e6;
		
		Intent intent = new Intent(C.ACTION.LOCATION_ALERT);
		intent.putExtra("row_id", taskId);
		intent.putExtra("task", task);
		
		PendingIntent proximityIntent = PendingIntent.getBroadcast(mContext,
				0,
				intent,
				PendingIntent.FLAG_UPDATE_CURRENT);
		
		// require -1 or millisecond (Long.maxValue is available)
		mLocationMgr.addProximityAlert(
				lat, lng, ALERT_RADIUS, -1, proximityIntent);		
		
		Log.e(TAG, " location alert of task " + taskId
				+ " is added on (" + lat + ", " + lng + ")");
	}
	
	private void updateWithNewLocation(Location location) {
		
		mLocation = location;
		
		if (mLocation == null)
			return;
			
		mLat = location.getLatitude();
		mLng = location.getLongitude();
		
		mAddress = getAddressByLatLng(mLat, mLng);
	}

	@Override
	public void onLocationChanged(Location location) {
		// TODO Auto-generated method stub
		
		Log.e(TAG, "location has been changed to ("
				+location.getLongitude()+", "+location.getLatitude()+")");
		
		updateWithNewLocation(location);
	}


	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		// TODO Auto-generated method stub
		
		Log.e(TAG, "provider status is changed!");
	}


	@Override
	public void onProviderEnabled(String provider) {
		
		mProviderEnabled = true;
		
		Intent i = new Intent(mContext, DWTService.class);
		mContext.bindService(i, this, Context.BIND_AUTO_CREATE);
		
		Log.e(TAG, "onProviderEnabled: provider is now enabled!");
	}


	@Override
	public void onProviderDisabled(String provider) {
		// TODO Auto-generated method stub
		
		updateWithNewLocation(null);
		
		mProviderEnabled = false;
		
		Intent i = new Intent(mContext, DWTService.class);
		mContext.bindService(i, this, Context.BIND_AUTO_CREATE);
		
		Log.e(TAG, "onProviderDisabled: provider is disabled!");
	}

	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
		// TODO Auto-generated method stub
		
		Log.e(TAG, "service is connected successfully!");
		
		MyBinder binder = (MyBinder) service;
		mService = binder.getService();
		mHandler = mService.getHander();
		
//		this.mLocationMgr = mService.getLocationManager();
		
		if (mProviderEnabled) {
			
			mHandler.sendEmptyMessage(C.GPS.GPS_ENABLED);
			
		} else {
			
			mHandler.sendEmptyMessage(C.GPS.GPS_DISABLED);
		}
		
		mContext.unbindService(this);		// Must unbind it
	}

	@Override
	public void onServiceDisconnected(ComponentName name) {
		// TODO Auto-generated method stub
		
		Log.e(TAG, "service is disconnected!");
		
		mService = null;
		mHandler = null;
	}
}
